Passed
Push — development ( 26403f...98b3d9 )
by Vad
08:03 queued 49s
created

BicyclesController   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Test Coverage

Coverage 85.71%

Importance

Changes 0
Metric Value
eloc 149
dl 0
loc 183
ccs 24
cts 28
cp 0.8571
rs 10
c 0
b 0
f 0
wmc 15

6 Functions

Rating   Name   Duplication   Size   Complexity  
A getBikeById 0 25 1
A getBicyclesByCity 0 21 1
A updateBicycle 0 33 1
A createManyBikes 0 26 3
B getAllBicycles 0 51 8
A createABike 0 17 1
1 6
import {
2
  Controller,
3
  Get,
4
  Post,
5
  Param,
6
  Patch,
7
  Body,
8
  Query,
9
  BadRequestException,
10
} from '@nestjs/common';
11 6
import {
12
  ApiBearerAuth,
13
  ApiOperation,
14
  ApiResponse,
15
  ApiParam,
16
  ApiBody,
17
  ApiTags,
18
  ApiQuery,
19
} from '@nestjs/swagger';
20
// we have removed all JwtAuthGuards from this route.
21
//  import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
22 6
import { BicyclesService } from './bicycles.service';
23 6
import { UpdateBicycleDto } from './dto/update-bicycle.dto';
24 6
import { Bicycle } from './entities/bicycle.entity';
25
import { BicycleResponse } from './types/bicycle-response.interface';
26 6
import { CreateBicycleDto } from './dto/create-bicycle.dto';
27 6
import { CityName } from 'src/cities/types/city.enum';
28
29 6
const BIKE_ID = 'b1e77dd3-9fb9-4e6c-a5c6-b6fc58f59464';
30 6
const BIKE_STATUS_AVAILABLE = 'Available';
0 ignored issues
show
introduced by
'BIKE_STATUS_AVAILABLE' is assigned a value but never used.
Loading history...
31 6
const CREATED_AT = '2024-12-01T05:01:01.000Z';
0 ignored issues
show
introduced by
'CREATED_AT' is assigned a value but never used.
Loading history...
32 6
const UPDATED_AT = '2024-12-07T18:30:30.000Z';
0 ignored issues
show
introduced by
'UPDATED_AT' is assigned a value but never used.
Loading history...
33 6
const UNAUTHORIZED_ERROR_MESSAGE = 'Unauthorized. Authentication required';
34 6
const CITY_ID_GOTHENBURG = '123e4567-e89b-12d3-a456-426614174000';
0 ignored issues
show
introduced by
'CITY_ID_GOTHENBURG' is assigned a value but never used.
Loading history...
35
36
@ApiTags('Bicycles')
37
@Controller({ path: 'bike', version: '1' })
38 6
export class BicyclesController {
39 8
  constructor(private readonly bicyclesService: BicyclesService) {}
40
41
  @Get()
42
  @ApiBearerAuth()
43
  @ApiOperation({ summary: 'Get all bicycles' })
44
  @ApiQuery({
45
    name: 'city',
46
    required: false,
47
    enum: CityName,
48
  })
49
  @ApiQuery({ name: 'lat', required: false, minimum: -90, maximum: 90 })
50
  @ApiQuery({ name: 'lon', required: false, minimum: -180, maximum: 180 })
51
  @ApiQuery({ name: 'radius', required: false, minimum: 0, maximum: 100000 })
52
  @ApiResponse({
53
    status: 200,
54
    description: 'List of bicycles',
55
    type: [Bicycle],
56
  })
57
  @ApiResponse({
58
    status: 401,
59
    description: 'Unauthorized. Authentication required',
60
  })
61 6
  async getAllBicycles(
62
    @Query('lat') lat?: string,
63
    @Query('lon') lon?: string,
64
    @Query('radius') radius?: string,
65
    @Query('city') city?: CityName,
66
  ): Promise<BicycleResponse[]> {
67 4
    const latitude = lat ? parseFloat(lat) : undefined;
68 4
    const longitude = lon ? parseFloat(lon) : undefined;
69 4
    const radi = radius ? parseFloat(radius) : 3000;
70
71 5
    if ((latitude && !longitude) || (longitude && !latitude)) {
72 1
      throw new BadRequestException('Both lat and lon must be provided for location search');
73
    }
74
75 3
    if (city) {
76 1
      if (latitude) {
77
        return this.bicyclesService.toBicycleResponses(
78
          await this.bicyclesService.findByCityAndLocation(city, latitude, longitude, radi),
79
        );
80
      }
81 1
      return this.bicyclesService.toBicycleResponses(await this.bicyclesService.findByCity(city));
82
    }
83
84 2
    if (latitude) {
85 1
      return this.bicyclesService.toBicycleResponses(
86
        await this.bicyclesService.findByLocation(latitude, longitude, radi),
87
      );
88
    }
89
90 1
    return this.bicyclesService.toBicycleResponses(await this.bicyclesService.findAll());
91
  }
92
93
  @Post('create')
94
  @ApiBearerAuth()
95
  @ApiOperation({ summary: 'Create a new bicycle' })
96
  @ApiBody({
97
    type: CreateBicycleDto,
98
    description: 'Bicycle creation data',
99
    required: false,
100
  })
101
  @ApiResponse({
102
    status: 201,
103
    description: 'Bicycle created successfully',
104
    type: Bicycle,
105
  })
106 6
  async createABike(@Body() createBicycleDto: CreateBicycleDto): Promise<Bicycle> {
107 1
    console.log('skapa cykel');
108 1
    return await this.bicyclesService.createBike(createBicycleDto);
109
  }
110
111
  @Post('create-many')
112
  @ApiBearerAuth()
113
  @ApiOperation({
114
    summary: 'Create multiple bicycles',
115
    description: 'Creates multiple bicycles in a single request. At least one bicycle must be provided.',
0 ignored issues
show
introduced by
Insert ⏎·····
Loading history...
116
  })
117
  @ApiBody({
118
    type: [CreateBicycleDto],
119
    description: 'Array of bicycle creation data',
120
    required: true,
121
  })
122
  @ApiResponse({
123
    status: 201,
124
    description: 'Bicycles created successfully',
125
    type: [Bicycle],
126
  })
127
  @ApiResponse({
128
    status: 400,
129
    description: 'Bad Request - Empty array or invalid bicycle data provided',
130
  })
131 6
  async createManyBikes(@Body() createBicycleDto: CreateBicycleDto[]): Promise<Bicycle[]> {
132 1
    if (!createBicycleDto?.length) {
133
      throw new BadRequestException('At least one bike is required');
134
    }
135
    return await this.bicyclesService.createManyBikes(createBicycleDto);
136
  }
137
138
  @Get(':bikeId')
139
  @ApiBearerAuth()
140
  @ApiOperation({ summary: 'Get a bicycle by ID' })
141
  @ApiParam({
142
    name: 'bikeId',
143
    description: 'Unique identifier of the bicycle',
144
    type: 'string',
145
    example: BIKE_ID,
146
  })
147
  @ApiResponse({
148
    status: 200,
149
    description: 'Bicycle details retrieved successfully',
150
    type: Bicycle,
151
  })
152
  @ApiResponse({
153
    status: 401,
154
    description: UNAUTHORIZED_ERROR_MESSAGE,
155
  })
156
  @ApiResponse({
157
    status: 404,
158
    description: 'Bicycle not found',
159
  })
160 6
  async getBikeById(@Param('bikeId') id: string): Promise<Bicycle> {
161 1
    return await this.bicyclesService.findById(id);
162
  }
163
164
  @Patch(':bikeId')
165
  @ApiBearerAuth()
166
  @ApiOperation({ summary: 'Update bicycle by ID' })
167
  @ApiParam({
168
    name: 'bikeId',
169
    description: 'Unique identifier of the bicycle',
170
    type: 'string',
171
    example: BIKE_ID,
172
  })
173
  @ApiBody({
174
    description: 'Bicycle update details',
175
    type: UpdateBicycleDto,
176
  })
177
  @ApiResponse({
178
    status: 200,
179
    description: 'Bicycle updated successfully',
180
    type: Bicycle
0 ignored issues
show
introduced by
Insert ,
Loading history...
181
  })
182
  @ApiResponse({
183
    status: 400,
184
    description: 'Invalid input',
185
  })
186
  @ApiResponse({
187
    status: 404,
188
    description: 'Bicycle not found',
189
  })
190
  @ApiResponse({
191
    status: 401,
192
    description: UNAUTHORIZED_ERROR_MESSAGE,
193
  })
194 6
  async updateBicycle(@Param('bikeId') bikeId: string, @Body() updateBicycleDto: UpdateBicycleDto) {
195 1
    return this.bicyclesService.update(bikeId, updateBicycleDto);
196
  }
197
198
  @Get('city/:cityName')
199
  @ApiBearerAuth()
200
  @ApiOperation({ summary: 'Get all bicycles in a specific city' })
201
  @ApiResponse({
202
    status: 200,
203
    description: 'List of bicycles in the specified city',
204
    type: [Bicycle],
205
  })
206
  @ApiResponse({
207
    status: 401,
208
    description: UNAUTHORIZED_ERROR_MESSAGE,
209
  })
210
  @ApiParam({
211
    name: 'cityName',
212
    description: 'Name of the city',
213
    type: 'string',
214
    enum: CityName,
215
  })
216 6
  async getBicyclesByCity(@Param('cityName') cityName: CityName): Promise<Bicycle[]> {
217
    return await this.bicyclesService.findByCity(cityName);
218
  }
219
}
220